﻿using System;
using System.Linq.Expressions;
using System.Reflection;
using MugenInjection.Infrastructure;
using MugenInjection.Interception;
using MugenInjection.Interception.Delegates;
using MugenInjection.Interception.Infrastructure;
using MugenInjection.Interception.Interface;
using MugenInjection.Interception.Syntax;
using MugenInjection.Interface;
using MugenInjection.Syntax;
using MugenInjection.Syntax.Constant;
using MugenInjection.Syntax.Method;
using MugenInjection.Syntax.Type;

// ReSharper disable CheckNamespace

namespace MugenInjection
// ReSharper restore CheckNamespace
{
    /// <summary>
    /// Represents the extensions for <see cref="IInjector"/> to add interceptor support.
    /// </summary>
    public static class InterceptionExtension
    {
        #region Extension method

        /// <summary>
        /// Indicates that instances associated with this binding will be proxied.
        /// </summary>
        /// <param name="bindingSyntax">The binding syntax target.</param>
        /// <param name="interceptorProcesses">The specified <see cref="IInterceptorProcess"/> for intercept.</param>
        /// <returns>The fluent syntax.</returns>
        public static IInterceptTypeSyntax Intercept(this ICommonBindingSyntax bindingSyntax,
                                                     params IInterceptorProcess[] interceptorProcesses)
        {
            return InterceptionExtensionUtils.Intercept(bindingSyntax, InjectorUtils.EmptyTypes, interceptorProcesses);
        }

        /// <summary>
        /// Indicates that instances associated with this binding will be proxied.
        /// </summary>
        /// <param name="bindingSyntax">The binding syntax target.</param>
        /// <param name="interfaces">Interfaces for proxy.</param>
        /// <param name="interceptorProcesses">The specified <see cref="IInterceptorProcess"/> for intercept.</param>
        /// <returns>The fluent syntax.</returns>
        public static IInterceptTypeSyntax Intercept(this ICommonBindingSyntax bindingSyntax, Type[] interfaces,
                                                     params IInterceptorProcess[] interceptorProcesses)
        {
            return InterceptionExtensionUtils.Intercept(bindingSyntax, interfaces, interceptorProcesses);
        }

        //------

        /// <summary>
        /// Indicates that instances associated with this binding will be proxied.
        /// </summary>
        /// <param name="bindingSyntax">The binding syntax target.</param>
        /// <param name="interceptorProcesses">The specified <see cref="IInterceptorProcess"/> for intercept.</param>
        /// <returns>The fluent syntax.</returns>
        public static IInterceptTypeSyntax<T> Intercept<T>(this IGenericBindingSyntax<T> bindingSyntax,
                                                           params IInterceptorProcess[] interceptorProcesses)
        {
            return InterceptionExtensionUtils.Intercept(bindingSyntax, InjectorUtils.EmptyTypes, interceptorProcesses);
        }

        /// <summary>
        /// Indicates that instances associated with this binding will be proxied.
        /// </summary>
        /// <param name="bindingSyntax">The binding syntax target.</param>
        /// <param name="interfaces">Interfaces for proxy.</param>
        /// <param name="interceptorProcesses">The specified <see cref="IInterceptorProcess"/> for intercept.</param>
        /// <returns>The fluent syntax.</returns>
        public static IInterceptTypeSyntax<T> Intercept<T>(this IGenericBindingSyntax<T> bindingSyntax, Type[] interfaces,
                                                           params IInterceptorProcess[] interceptorProcesses)
        {
            return InterceptionExtensionUtils.Intercept(bindingSyntax, interfaces, interceptorProcesses);
        }

        /// <summary>
        /// Indicates that instances associated with this binding will be proxied and the target will be to the original binding.
        /// </summary>
        /// <param name="constantObjectSyntax">The binding syntax target.</param>
        /// <param name="interfaces">Interfaces for proxy.</param>
        /// <param name="interceptorProcesses">The specified <see cref="IInterceptorProcess"/> for intercept.</param>
        /// <returns>The fluent syntax.</returns>
        public static IInterceptAsTargetConstantSyntax InterceptAsTarget(this IConstantObjectSyntax constantObjectSyntax, Type[] interfaces, params IInterceptorProcess[] interceptorProcesses)
        {
            return InterceptionExtensionUtils.InterceptAsTarget(constantObjectSyntax, interfaces, interceptorProcesses);
        }

        /// <summary>
        /// Indicates that instances associated with this binding will be proxied and the target will be to the original binding.
        /// </summary>
        /// <param name="constantObjectSyntax">The binding syntax target.</param>
        /// <param name="interceptorProcesses">The specified <see cref="IInterceptorProcess"/> for intercept.</param>
        /// <returns>The fluent syntax.</returns>
        public static IInterceptAsTargetConstantSyntax InterceptAsTarget(this IConstantObjectSyntax constantObjectSyntax, params IInterceptorProcess[] interceptorProcesses)
        {
            return InterceptionExtensionUtils.InterceptAsTarget(constantObjectSyntax, InjectorUtils.EmptyTypes,
                                                                interceptorProcesses);
        }

        /// <summary>
        /// Indicates that instances associated with this binding will be proxied and the target will be to the original binding.
        /// </summary>
        /// <param name="methodObjectSyntax">The binding syntax target.</param>
        /// <param name="interfaces">Interfaces for proxy.</param>
        /// <param name="interceptorProcesses">The specified <see cref="IInterceptorProcess"/> for intercept.</param>
        /// <returns>The fluent syntax.</returns>
        public static IInterceptAsTargetMethodSyntax InterceptAsTarget(this IMethodObjectSyntax methodObjectSyntax, Type[] interfaces, params IInterceptorProcess[] interceptorProcesses)
        {
            return InterceptionExtensionUtils.InterceptAsTarget(methodObjectSyntax, interfaces, interceptorProcesses);
        }

        /// <summary>
        /// Indicates that instances associated with this binding will be proxied and the target will be to the original binding.
        /// </summary>
        /// <param name="methodObjectSyntax">The binding syntax target.</param>
        /// <param name="interceptorProcesses">The specified <see cref="IInterceptorProcess"/> for intercept.</param>
        /// <returns>The fluent syntax.</returns>
        public static IInterceptAsTargetMethodSyntax InterceptAsTarget(this IMethodObjectSyntax methodObjectSyntax, params IInterceptorProcess[] interceptorProcesses)
        {
            return InterceptionExtensionUtils.InterceptAsTarget(methodObjectSyntax, InjectorUtils.EmptyTypes,
                                                                interceptorProcesses);
        }

        /// <summary>
        /// Indicates that instances associated with this binding will be proxied and the target will be to the original binding.
        /// </summary>
        /// <param name="typeObjectSyntax">The binding syntax target.</param>
        /// <param name="interfaces">Interfaces for proxy.</param>
        /// <param name="interceptorProcesses">The specified <see cref="IInterceptorProcess"/> for intercept.</param>
        /// <returns>The fluent syntax.</returns>
        public static IInterceptAsTargetTypeSyntax InterceptAsTarget(this ITypeObjectSyntax typeObjectSyntax, Type[] interfaces, params IInterceptorProcess[] interceptorProcesses)
        {
            return InterceptionExtensionUtils.InterceptAsTarget(typeObjectSyntax, interfaces, interceptorProcesses);
        }

        /// <summary>
        /// Indicates that instances associated with this binding will be proxied and the target will be to the original binding.
        /// </summary>
        /// <param name="typeObjectSyntax">The binding syntax target.</param>
        /// <param name="interceptorProcesses">The specified <see cref="IInterceptorProcess"/> for intercept.</param>
        /// <returns>The fluent syntax.</returns>
        public static IInterceptAsTargetTypeSyntax InterceptAsTarget(this ITypeObjectSyntax typeObjectSyntax, params IInterceptorProcess[] interceptorProcesses)
        {
            return InterceptionExtensionUtils.InterceptAsTarget(typeObjectSyntax, InjectorUtils.EmptyTypes,
                                                                interceptorProcesses);
        }

        //------------------------

        /// <summary>
        /// Indicates that instances associated with this binding will be proxied and the target will be to the original binding.
        /// </summary>
        /// <param name="constantObjectSyntax">The binding syntax target.</param>
        /// <param name="interfaces">Interfaces for proxy.</param>
        /// <param name="interceptorProcesses">The specified <see cref="IInterceptorProcess"/> for intercept.</param>
        /// <returns>The fluent syntax.</returns>
        public static IInterceptAsTargetConstantSyntax<T> InterceptAsTarget<T>(this 
            IConstantObjectSyntax<T> constantObjectSyntax, Type[] interfaces,
            params IInterceptorProcess[] interceptorProcesses)
        {
            return InterceptionExtensionUtils.InterceptAsTarget(constantObjectSyntax, interfaces, interceptorProcesses);
        }

        /// <summary>
        /// Indicates that instances associated with this binding will be proxied and the target will be to the original binding.
        /// </summary>
        /// <param name="constantObjectSyntax">The binding syntax target.</param>
        /// <param name="interceptorProcesses">The specified <see cref="IInterceptorProcess"/> for intercept.</param>
        /// <returns>The fluent syntax.</returns>
        public static IInterceptAsTargetConstantSyntax<T> InterceptAsTarget<T>(this 
            IConstantObjectSyntax<T> constantObjectSyntax,
            params IInterceptorProcess[] interceptorProcesses)
        {
            return InterceptionExtensionUtils.InterceptAsTarget(constantObjectSyntax, InjectorUtils.EmptyTypes,
                                                                interceptorProcesses);
        }

        /// <summary>
        /// Indicates that instances associated with this binding will be proxied and the target will be to the original binding.
        /// </summary>
        /// <param name="methodObjectSyntax">The binding syntax target.</param>
        /// <param name="interfaces">Interfaces for proxy.</param>
        /// <param name="interceptorProcesses">The specified <see cref="IInterceptorProcess"/> for intercept.</param>
        /// <returns>The fluent syntax.</returns>
        public static IInterceptAsTargetMethodSyntax<T> InterceptAsTarget<T>(this IMethodObjectSyntax<T> methodObjectSyntax,
                                                                             Type[] interfaces,
                                                                             params IInterceptorProcess[]
                                                                                 interceptorProcesses)
        {
            return InterceptionExtensionUtils.InterceptAsTarget(methodObjectSyntax, interfaces, interceptorProcesses);
        }

        /// <summary>
        /// Indicates that instances associated with this binding will be proxied and the target will be to the original binding.
        /// </summary>
        /// <param name="methodObjectSyntax">The binding syntax target.</param>
        /// <param name="interceptorProcesses">The specified <see cref="IInterceptorProcess"/> for intercept.</param>
        /// <returns>The fluent syntax.</returns>
        public static IInterceptAsTargetMethodSyntax<T> InterceptAsTarget<T>(this IMethodObjectSyntax<T> methodObjectSyntax,
                                                                             params IInterceptorProcess[]
                                                                                 interceptorProcesses)
        {
            return InterceptionExtensionUtils.InterceptAsTarget(methodObjectSyntax, InjectorUtils.EmptyTypes,
                                                                interceptorProcesses);
        }

        /// <summary>
        /// Indicates that instances associated with this binding will be proxied and the target will be to the original binding.
        /// </summary>
        /// <param name="typeObjectSyntax">The binding syntax target.</param>
        /// <param name="interfaces">Interfaces for proxy.</param>
        /// <param name="interceptorProcesses">The specified <see cref="IInterceptorProcess"/> for intercept.</param>
        /// <returns>The fluent syntax.</returns>
        public static IInterceptAsTargetTypeSyntax<T> InterceptAsTarget<T>(this ITypeObjectSyntax<T> typeObjectSyntax,
                                                                           Type[] interfaces,
                                                                           params IInterceptorProcess[]
                                                                               interceptorProcesses)
        {
            return InterceptionExtensionUtils.InterceptAsTarget(typeObjectSyntax, interfaces, interceptorProcesses);
        }

        /// <summary>
        /// Indicates that instances associated with this binding will be proxied and the target will be to the original binding.
        /// </summary>
        /// <param name="typeObjectSyntax">The binding syntax target.</param>
        /// <param name="interceptorProcesses">The specified <see cref="IInterceptorProcess"/> for intercept.</param>
        /// <returns>The fluent syntax.</returns>
        public static IInterceptAsTargetTypeSyntax<T> InterceptAsTarget<T>(this ITypeObjectSyntax<T> typeObjectSyntax,
                                                                           params IInterceptorProcess[]
                                                                               interceptorProcesses)
        {
            return InterceptionExtensionUtils.InterceptAsTarget(typeObjectSyntax, InjectorUtils.EmptyTypes,
                                                                interceptorProcesses);
        }


        //------------

        /// <summary>
        /// Intercepts a get property method.
        /// </summary>
        /// <param name="interceptSyntax">The binding syntax target.</param>
        /// <param name="propertyInfo">The specified <see cref="PropertyInfo"/> for intercept.</param>
        /// <param name="interceptAction">The specified delegate for intercept.</param>
        /// <returns>The fluent syntax.</returns>
        public static TSyntax InterceptGetProperty<TSyntax>(this TSyntax interceptSyntax, PropertyInfo propertyInfo, Func<IPropertyGetInterceptor, object> interceptAction)
            where TSyntax : IInterceptSyntax
        {
            return InterceptionExtensionUtils.InterceptGetProperty(interceptSyntax, propertyInfo, interceptAction);
        }

        /// <summary>
        /// Intercepts a set property method.
        /// </summary>
        /// <param name="interceptSyntax">The binding syntax target.</param>
        /// <param name="propertyInfo">The specified <see cref="PropertyInfo"/> for intercept.</param>
        /// <param name="interceptAction">The specified delegate for intercept.</param>
        /// <returns>The fluent syntax.</returns>
        public static TSyntax InterceptSetProperty<TSyntax>(this TSyntax interceptSyntax, PropertyInfo propertyInfo, Func<IPropertySetInterceptor, object> interceptAction)
            where TSyntax : IInterceptSyntax
        {
            return InterceptionExtensionUtils.InterceptSetProperty(interceptSyntax, propertyInfo, interceptAction);
        }

        /// <summary>
        /// Intercepts adding an event.
        /// </summary>
        /// <param name="interceptSyntax">The binding syntax target.</param>
        /// <param name="eventInfo">The specified <see cref="EventInfo"/> for intercept.</param>
        /// <param name="interceptAction">The specified delegate for intercept.</param>
        /// <returns>The fluent syntax.</returns>
        public static TSyntax InterceptAddEvent<TSyntax>(this TSyntax interceptSyntax, EventInfo eventInfo, Func<IEventAddInterceptor, Delegate> interceptAction)
            where TSyntax : IInterceptSyntax
        {
            return InterceptionExtensionUtils.InterceptAddEvent(interceptSyntax, eventInfo, interceptAction);
        }

        /// <summary>
        /// Intercepts removing an event.
        /// </summary>
        /// <param name="interceptSyntax">The binding syntax target.</param>
        /// <param name="eventInfo">The specified <see cref="EventInfo"/> for intercept.</param>
        /// <param name="interceptAction">The specified delegate for intercept.</param>
        /// <returns>The fluent syntax.</returns>
        public static TSyntax InterceptRemoveEvent<TSyntax>(this TSyntax interceptSyntax, EventInfo eventInfo, Func<IEventRemoveInterceptor, Delegate> interceptAction)
            where TSyntax : IInterceptSyntax
        {
            return InterceptionExtensionUtils.InterceptRemoveEvent(interceptSyntax, eventInfo, interceptAction);
        }

        /// <summary>
        /// Intercept a method invoke.
        /// </summary>
        /// <param name="interceptSyntax">The binding syntax target.</param>
        /// <param name="methodInfo">The specified <see cref="MethodInfo"/> for intercept.</param>
        /// <param name="interceptAction">The specified delegate for intercept.</param>
        /// <returns>The fluent syntax.</returns>
        public static TSyntax InterceptInvokeMethod<TSyntax>(this TSyntax interceptSyntax, MethodInfo methodInfo, Func<IMethodInterceptor, object> interceptAction)
            where TSyntax : IInterceptSyntax
        {
            return InterceptionExtensionUtils.InterceptInvokeMethod(interceptSyntax, methodInfo, interceptAction);
        }

        //------------

        /// <summary>
        /// Intercepts a get property method.
        /// </summary>
        /// <param name="interceptSyntax">The binding syntax target.</param>
        /// <param name="property">The property accesss expression.</param>
        /// <param name="interceptAction">The specified delegate for intercept.</param>
        /// <returns>The fluent syntax.</returns>
        public static IInterceptAsTargetConstantSyntax<T> InterceptGetProperty<T, TValue>(this IInterceptAsTargetConstantSyntax<T> interceptSyntax, Expression<Func<T, TValue>> property, GetPropertyDelegate<TValue, T> interceptAction)
        {
            PropertyInfo propertyInfo = InjectorUtilsInternal.GetProperty(property);
            return InterceptionExtensionUtils
                .InterceptGetProperty(interceptSyntax, propertyInfo,
                                      interceptor => interceptAction((T)interceptor.Target,
                                                                     () =>
                                                                     {
                                                                         interceptor.GetMethod();
                                                                         return (TValue)interceptor.GetValue;
                                                                     }, interceptor));
        }

        /// <summary>
        /// Intercepts a get property method.
        /// </summary>
        /// <param name="interceptSyntax">The binding syntax target.</param>
        /// <param name="property">The property accesss expression.</param>
        /// <param name="interceptAction">The specified delegate for intercept.</param>
        /// <returns>The fluent syntax.</returns>
        public static IInterceptAsTargetMethodSyntax<T> InterceptGetProperty<T, TValue>(this IInterceptAsTargetMethodSyntax<T> interceptSyntax, Expression<Func<T, TValue>> property, GetPropertyDelegate<TValue, T> interceptAction)
        {
            PropertyInfo propertyInfo = InjectorUtilsInternal.GetProperty(property);
            return InterceptionExtensionUtils
                .InterceptGetProperty(interceptSyntax, propertyInfo,
                                      interceptor => interceptAction((T)interceptor.Target,
                                                                     () =>
                                                                     {
                                                                         interceptor.GetMethod();
                                                                         return (TValue)interceptor.GetValue;
                                                                     }, interceptor));
        }

        /// <summary>
        /// Intercepts a get property method.
        /// </summary>
        /// <param name="interceptSyntax">The binding syntax target.</param>
        /// <param name="property">The property accesss expression.</param>
        /// <param name="interceptAction">The specified delegate for intercept.</param>
        /// <returns>The fluent syntax.</returns>
        public static IInterceptAsTargetTypeSyntax<T> InterceptGetProperty<T, TValue>(this IInterceptAsTargetTypeSyntax<T> interceptSyntax, Expression<Func<T, TValue>> property, GetPropertyDelegate<TValue, T> interceptAction)
        {
            PropertyInfo propertyInfo = InjectorUtilsInternal.GetProperty(property);
            return InterceptionExtensionUtils
                .InterceptGetProperty(interceptSyntax, propertyInfo,
                                      interceptor => interceptAction((T)interceptor.Target,
                                                                     () =>
                                                                     {
                                                                         interceptor.GetMethod();
                                                                         return (TValue)interceptor.GetValue;
                                                                     }, interceptor));
        }

        /// <summary>
        /// Intercepts a set property method.
        /// </summary>
        /// <param name="interceptSyntax">The binding syntax target.</param>
        /// <param name="property">The property accesss expression.</param>
        /// <param name="interceptAction">The specified delegate for intercept.</param>
        /// <returns>The fluent syntax.</returns>
        public static IInterceptAsTargetConstantSyntax<T> InterceptSetProperty<T, TValue>(this IInterceptAsTargetConstantSyntax<T> interceptSyntax, Expression<Func<T, TValue>> property, SetPropertyDelegate<TValue, T> interceptAction)
        {
            PropertyInfo propertyInfo = InjectorUtilsInternal.GetProperty(property);
            return InterceptionExtensionUtils.InterceptSetProperty(interceptSyntax, propertyInfo, interceptor => interceptAction((T)interceptor.Target, (TValue)interceptor.SetValue, interceptor));
        }

        /// <summary>
        /// Intercepts a set property method.
        /// </summary>
        /// <param name="interceptSyntax">The binding syntax target.</param>
        /// <param name="property">The property accesss expression.</param>
        /// <param name="interceptAction">The specified delegate for intercept.</param>
        /// <returns>The fluent syntax.</returns>
        public static IInterceptAsTargetMethodSyntax<T> InterceptSetProperty<T, TValue>(this IInterceptAsTargetMethodSyntax<T> interceptSyntax, Expression<Func<T, TValue>> property, SetPropertyDelegate<TValue, T> interceptAction)
        {
            PropertyInfo propertyInfo = InjectorUtilsInternal.GetProperty(property);
            return InterceptionExtensionUtils.InterceptSetProperty(interceptSyntax, propertyInfo, interceptor => interceptAction((T)interceptor.Target, (TValue)interceptor.SetValue, interceptor));
        }

        /// <summary>
        /// Intercepts a set property method.
        /// </summary>
        /// <param name="interceptSyntax">The binding syntax target.</param>
        /// <param name="property">The property accesss expression.</param>
        /// <param name="interceptAction">The specified delegate for intercept.</param>
        /// <returns>The fluent syntax.</returns>
        public static IInterceptAsTargetTypeSyntax<T> InterceptSetProperty<T, TValue>(this IInterceptAsTargetTypeSyntax<T> interceptSyntax, Expression<Func<T, TValue>> property, SetPropertyDelegate<TValue, T> interceptAction)
        {
            PropertyInfo propertyInfo = InjectorUtilsInternal.GetProperty(property);
            return InterceptionExtensionUtils.InterceptSetProperty(interceptSyntax, propertyInfo, interceptor => interceptAction((T)interceptor.Target, (TValue)interceptor.SetValue, interceptor));
        }

        /// <summary>
        /// Intercepts a method invoke.
        /// </summary>
        /// <param name="interceptSyntax">The binding syntax target.</param>
        /// <param name="voidMethodCall">The specified method call expression.</param>
        /// <param name="interceptAction">The specified delegate for intercept.</param>
        /// <returns>The fluent syntax.</returns>
        public static IInterceptAsTargetConstantSyntax<T> InterceptInvokeMethod<T>(this IInterceptAsTargetConstantSyntax<T> interceptSyntax, Expression<Action<AnyResolveContext, T>> voidMethodCall, InvokeVoidMethodDelegate<T> interceptAction)
        {
            MethodCallExpression methodCallExpression = InjectorUtilsInternal.ParseMethodExpression(voidMethodCall);
            return InterceptionExtensionUtils
                .InterceptInvokeMethod(interceptSyntax, methodCallExpression.Method,
                                       interceptor =>
                                       {
                                           interceptAction((T)interceptor.Target, interceptor.Method, interceptor);
                                           return null;
                                       });
        }

        /// <summary>
        /// Intercepts a method invoke.
        /// </summary>
        /// <param name="interceptSyntax">The binding syntax target.</param>
        /// <param name="voidMethodCall">The specified method call expression.</param>
        /// <param name="interceptAction">The specified delegate for intercept.</param>
        /// <returns>The fluent syntax.</returns>
        public static IInterceptAsTargetMethodSyntax<T> InterceptInvokeMethod<T>(this IInterceptAsTargetMethodSyntax<T> interceptSyntax, Expression<Action<AnyResolveContext, T>> voidMethodCall, InvokeVoidMethodDelegate<T> interceptAction)
        {
            MethodCallExpression methodCallExpression = InjectorUtilsInternal.ParseMethodExpression(voidMethodCall);
            return InterceptionExtensionUtils
                .InterceptInvokeMethod(interceptSyntax, methodCallExpression.Method,
                                       interceptor =>
                                       {
                                           interceptAction((T)interceptor.Target, interceptor.Method, interceptor);
                                           return null;
                                       });
        }

        /// <summary>
        /// Intercepts a method invoke.
        /// </summary>
        /// <param name="interceptSyntax">The binding syntax target.</param>
        /// <param name="voidMethodCall">The specified method call expression.</param>
        /// <param name="interceptAction">The specified delegate for intercept.</param>
        /// <returns>The fluent syntax.</returns>
        public static IInterceptAsTargetTypeSyntax<T> InterceptInvokeMethod<T>(this IInterceptAsTargetTypeSyntax<T> interceptSyntax, Expression<Action<AnyResolveContext, T>> voidMethodCall, InvokeVoidMethodDelegate<T> interceptAction)
        {
            MethodCallExpression methodCallExpression = InjectorUtilsInternal.ParseMethodExpression(voidMethodCall);
            return InterceptionExtensionUtils
                .InterceptInvokeMethod(interceptSyntax, methodCallExpression.Method,
                                       interceptor =>
                                       {
                                           interceptAction((T)interceptor.Target, interceptor.Method, interceptor);
                                           return null;
                                       });
        }

        /// <summary>
        /// Intercepts a method invoke.
        /// </summary>
        /// <param name="interceptSyntax">The binding syntax target.</param>
        /// <param name="methodCall">The specified method call expression.</param>
        /// <param name="interceptAction">The specified delegate for intercept.</param>
        /// <returns>The fluent syntax.</returns>
        public static IInterceptAsTargetConstantSyntax<T> InterceptInvokeMethod<T, TValue>(this IInterceptAsTargetConstantSyntax<T> interceptSyntax,
            Expression<Func<AnyResolveContext, T, TValue>> methodCall, InvokeMethodDelegate<TValue, T> interceptAction)
        {
            MethodCallExpression methodCallExpression = InjectorUtilsInternal.ParseMethodExpression(methodCall);
            return InterceptionExtensionUtils
                .InterceptInvokeMethod(interceptSyntax, methodCallExpression.Method,
                                       interceptor => interceptAction((T)interceptor.Target, () =>
                                           {
                                               interceptor.Method();
                                               return (TValue)interceptor.ReturnValue;
                                           }, interceptor));
        }

        /// <summary>
        /// Intercepts a method invoke.
        /// </summary>
        /// <param name="interceptSyntax">The binding syntax target.</param>
        /// <param name="methodCall">The specified method call expression.</param>
        /// <param name="interceptAction">The specified delegate for intercept.</param>
        /// <returns>The fluent syntax.</returns>
        public static IInterceptAsTargetMethodSyntax<T> InterceptInvokeMethod<T, TValue>(this IInterceptAsTargetMethodSyntax<T> interceptSyntax,
            Expression<Func<AnyResolveContext, T, TValue>> methodCall, InvokeMethodDelegate<TValue, T> interceptAction)
        {
            MethodCallExpression methodCallExpression = InjectorUtilsInternal.ParseMethodExpression(methodCall);
            return InterceptionExtensionUtils
                .InterceptInvokeMethod(interceptSyntax, methodCallExpression.Method,
                                       interceptor => interceptAction((T)interceptor.Target, () =>
                                       {
                                           interceptor.Method();
                                           return (TValue)interceptor.ReturnValue;
                                       }, interceptor));
        }

        /// <summary>
        /// Intercepts a method invoke.
        /// </summary>
        /// <param name="interceptSyntax">The binding syntax target.</param>
        /// <param name="methodCall">The specified method call expression.</param>
        /// <param name="interceptAction">The specified delegate for intercept.</param>
        /// <returns>The fluent syntax.</returns>
        public static IInterceptAsTargetTypeSyntax<T> InterceptInvokeMethod<T, TValue>(this IInterceptAsTargetTypeSyntax<T> interceptSyntax,
            Expression<Func<AnyResolveContext, T, TValue>> methodCall, InvokeMethodDelegate<TValue, T> interceptAction)
        {
            MethodCallExpression methodCallExpression = InjectorUtilsInternal.ParseMethodExpression(methodCall);
            return InterceptionExtensionUtils
                .InterceptInvokeMethod(interceptSyntax, methodCallExpression.Method,
                                       interceptor => interceptAction((T)interceptor.Target, () =>
                                       {
                                           interceptor.Method();
                                           return (TValue)interceptor.ReturnValue;
                                       }, interceptor));
        }

        #endregion
    }
}